﻿using Client.Base;
using Cysharp.Threading.Tasks;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;

namespace Client.Hotfix
{
    [SSM(SSMWeight.NetService)]
    public class NetService : IAwake, ITick, IDestroy
    {
        public BaseWSClient<C2GWSSession> C2G_WS_Client { get; private set; }  //客户端-游戏服 链接
        #region 其他服
        //public BaseWSClient<C2SSession> C2ChatClient { get; private set; } //客户端-聊天服 链接(暂无此功能)
        //public BaseWSClient<C2SSession> C2LClient { get; private set; } //客户端-登录服 链接(暂无此功能)
        #endregion

        private readonly Dictionary<int, Action<GameMessage>> messageDict = new();

        private bool isActiveCloseConnect = false; //主动断开链接标志位,用来处理是否需要重连操作
        private bool isReconnting = false;
        private int heartbeatTimerID = -1;
        private const uint HeartbeatInterval = 5000; //5s ping一次
        private const uint TimeoutInterval = HeartbeatInterval + 3000; // 3秒内无响应视为链接断开，开始处理重建
        private CancellationTokenSource cts;
        private CancellationToken ct;
        private DateTime lastActivity;
        private bool enableHeartbeat = false;
        private GameMessage heartbeatMessage ;


        #region 生命周期
        public void Awake(params object[] args)
        {
            this.Log("[ Service ] - NetService is running...");
            heartbeatMessage = new GameMessage { ReqId = 1 };
            ReqHeartBeat_1 req = new ReqHeartBeat_1();
            heartbeatMessage.Payload = req.ToByteString();

            Api.Instance.Get<EventService>().AddListener(EventCode.Event_LoginResult, arg => 
            {
                enableHeartbeat = (bool)arg;
            });
        }

        public void Tick()
        {
            C2G_WS_Client?.Tick();
        }
        public void Destroy()
        {
            Api.Instance.Get<EventService>().RemoveListenerByClass(this);
            C2G_WS_Client?.Destroy();
        }
        #endregion


        #region 链接
        /// <summary>
        /// 链接webSocket
        /// </summary>
        /// <param name="url"></param>
        public void Connect(string url)
        {
            C2G_WS_Client = new(url,MessageHandle);
            C2G_WS_Client.Awake();
        }

        /// <summary>
        /// 主动断开
        /// </summary>
        public void Disconnect()
        {
            if(C2G_WS_Client!=null && C2G_WS_Client.IsConnected)
            {
                isActiveCloseConnect = true;
                C2G_WS_Client.IsConnected = false;
                C2G_WS_Client?.Destroy();
                C2G_WS_Client = null;
            }
        }

        public void OnConnectedHandle()
        {
            cts?.Cancel();
            C2G_WS_Client.IsConnected = true;
            isReconnting = false; 
            if (C2G_WS_Client.curReconectCount > 0)  //重连成功
            {
                Api.Instance.Get<EventService>().Call(EventCode.Event_ReconnectedServer);
                this.Log("connect server successfull .");
            }
            else //正常链接成功
            {
                this.Log("reconnect server successfull .");
                Api.Instance.Get<EventService>().Call(EventCode.Event_ConnectedServer);
            }

           
                //心跳处理
                Api.Instance.Get<TimerService>().DelTimerTask(heartbeatTimerID);
                lastActivity = DateTime.UtcNow;
                heartbeatTimerID = Api.Instance.Get<TimerService>().AddTimerTask(HeartbeatInterval, tid =>
                {
                    if (enableHeartbeat)
                    {
                        CheckNetActivity();
                        SendHeartbeatMessage();
                    }
                }, null, 0);
            
            


            C2G_WS_Client.curReconectCount = 0;
        }

        private void CheckNetActivity()
        {
            var inactiveTime = (DateTime.UtcNow - lastActivity).TotalMilliseconds;
            if (inactiveTime > TimeoutInterval)
            {
                this.Log("heartbeart timeOut, disconnted!");
                //关闭心跳
                Api.Instance.Get<TimerService>().DelTimerTask(heartbeatTimerID);
                StartReconnect();
            }
        }

        /// <summary>
        /// 开始重连
        /// </summary>
        private async void StartReconnect()
        {
            if (isReconnting) //避免重复操作
                return;
            isReconnting = true;
            C2G_WS_Client.IsConnected = false;
            C2G_WS_Client.Destroy(); //关闭
            cts = new CancellationTokenSource();
            ct = cts.Token;
            while (true)
            {
                if (ct.IsCancellationRequested)
                    break;
                if (C2G_WS_Client.curReconectCount >= C2G_WS_Client.maxReconnectCount)
                    break;
                C2G_WS_Client.Reconnect();
                await UniTask.Delay(3000);
            }
            if (C2G_WS_Client.curReconectCount >= C2G_WS_Client.maxReconnectCount) //重连失败
            {
                this.Log("reconnect fail !");
                isReconnting = false;
                Api.Instance.Get<EventService>().Call(EventCode.Event_ReconnectedServerFail);
                C2G_WS_Client.curReconectCount = 0;
            }
        }

        private void SendHeartbeatMessage()
        {
            heartbeatMessage.Send();
        }

        public void OnDisconnectedHandle()
        {
            Api.Instance.Get<EventService>().Call(EventCode.Event_DisconnectServer);
            if (isActiveCloseConnect) //如果是主动断开则不处理重连操作
            {
                this.Log("self disconnect");
                isActiveCloseConnect = false;
                return; 
            }
            StartReconnect();
        }

        #endregion
        /// <summary> 监听消息 </summary>
        public void AddMessageListener(int cmd ,Action<GameMessage> handle)
        {
            messageDict.TryAdd(cmd, handle);
        }


        /// <summary> 移除监听消息 </summary>
        public void RemoveMessageListener(int cmd, Action<GameMessage> handle)
        {
            if (messageDict.ContainsKey(cmd))
            {
                messageDict.Remove(cmd);
            }
        }


        private void MessageHandle(int cmd, GameMessage message)
        {
            if (message.ReqId != 1) //过滤心跳日志
            {
                message.Print();
            }
           
            lastActivity = DateTime.UtcNow; //收到消息的时候记录当前时间
            if (messageDict.ContainsKey(cmd))
            {
                messageDict[cmd]?.Invoke(message);
            }
        }

    }

}
